// (C) Copyright 2012 Kystar. All rights reserved.

`timescale 1ns/100ps
`default_nettype none

module vib_top 
#(parameter
    MAX_VIN_WIDTH = 0,
    FRAME1_START_ADDR = 0
)
(
    // global signals
    input  wire          I_sclk,
    input  wire          I_rst_n,

    // video input
    input  wire          I_vin_pclk,
    input  wire          I_vin_vsync,
    input  wire          I_vin_de,
    input  wire [ 23: 0] I_vin_data,

    output wire          O_vin_led,

    // control signals
    output reg           O_new_frame,
    output reg           O_vib_use_c1,

    // memory interface
    output wire          O_sdm1_vib_req,
    input  wire          I_sdm1_vib_ack,
    input  wire          I_sdm1_sdram_wr_rd_end,
    output wire          O_sdm1_sdram_wr_en,
    output wire [ 31: 0] O_sdm1_sdram_start_addr,
    output wire [ 15: 0] O_sdm1_sdram_length,
    input  wire          I_sdm1_sdram_ask_for_data,
    output wire [ 23: 0] O_sdm1_sdram_wdata,

    output wire          O_sdm2_vib_req,
    input  wire          I_sdm2_vib_ack,
    input  wire          I_sdm2_sdram_wr_rd_end,
    output wire          O_sdm2_sdram_wr_en,
    output wire [ 31: 0] O_sdm2_sdram_start_addr,
    output wire [ 15: 0] O_sdm2_sdram_length,
    input  wire          I_sdm2_sdram_ask_for_data,
    output wire [ 23: 0] O_sdm2_sdram_wdata,

    // registers
    input  wire          I_reg_vib_enable,
    output wire [ 12: 0] O_reg_rb_vin_width,
    output wire [ 12: 0] O_reg_rb_vin_height,
    output wire [ 12: 0] O_reg_rb_vin_width_to_pc,
    output wire [ 12: 0] O_reg_rb_vin_height_to_pc,
    output wire [  7: 0] O_reg_rb_vin_frame_rate,
    output wire          O_reg_rb_video_not_active,
    output reg           O_reg_rb_ddr3_err,
    input  wire [ 11: 0] I_reg_px_start_row_offset,
    input  wire [ 11: 0] I_reg_px_start_col_offset,
    input  wire [ 11: 0] I_reg_vin_max_width,
    input  wire [ 11: 0] I_reg_vin_max_height

);

/******************************************************************************
                                <localparams>
******************************************************************************/
localparam MEM_WR_INSTR = 3'b010;

localparam // arb_state
    IDLE = 0,
    ARBI_0 = 1,
    ARBI_1 = 1<<1;

/******************************************************************************
                              <internal signals>
******************************************************************************/
wire pclk;
wire vin_de;
wire [ 23: 0] vin_data;
reg  de;
reg  [ 23: 0] data;
wire new_frame;
reg  [  7: 0] new_frame_dly;
reg  even_line;
reg  fifo_wrreq;
reg  fifo_0_wrreq;
reg  fifo_1_wrreq;
reg  [ 23: 0] fifo_wdata;
reg  [ 23: 0] fifo_wdata_dly;
wire fifo0_rdreq;
wire [ 23: 0] fifo0_q;
wire [ 12: 0] fifo0_usedw;
wire fifo1_rdreq;
wire [ 23: 0] fifo1_q;
wire [ 12: 0] fifo1_usedw;
reg  reset_fifo_n;
reg  even_line_pre;
reg  [ 7: 0] even_line_pre_dly;
reg  [ 3: 0] vin_vsync_pclk_dly;
reg  [ 11: 0] de_cnt;
wire vin_vsync;
reg  [ 12: 0] reg_vin_width;
reg  [ 12: 0] reg_vin_height;
wire reg_rb_ddr3_err_0;
wire reg_rb_ddr3_err_1;

/******************************************************************************
                                <module body>
******************************************************************************/
//--------------------------------------------------------------------
// vin_fmt_detect
//--------------------------------------------------------------------
vin_io
#(
    .MAX_VIN_WIDTH(MAX_VIN_WIDTH)
)
u_vin_io
(
    .I_sclk(I_sclk),
    .I_rst_n(I_rst_n),
    .I_vin_pclk(I_vin_pclk),
    .I_vin_vsync(I_vin_vsync),
    .I_vin_de(I_vin_de),
    .I_vin_data(I_vin_data),
    .O_vin_pclk(pclk),
    .O_vin_vsync(vin_vsync),
    .O_vin_de(vin_de),
    .O_vin_data(vin_data),
    .I_reg_px_start_row_offset(I_reg_px_start_row_offset),
    .I_reg_px_start_col_offset(I_reg_px_start_col_offset),
    .I_reg_vin_max_width(I_reg_vin_max_width),
    .I_reg_vin_max_height(I_reg_vin_max_height),
    .O_reg_rb_vin_width(O_reg_rb_vin_width_to_pc),
    .O_reg_rb_vin_height(O_reg_rb_vin_height_to_pc),
    .O_reg_rb_vin_frame_rate(O_reg_rb_vin_frame_rate),
    .O_reg_rb_video_not_active(O_reg_rb_video_not_active),
    .O_vin_led(O_vin_led)
);

vin_fmt_detect
#(
    .MAX_IN_WIDTH(MAX_VIN_WIDTH),
    .MAX_IN_HEIGHT(4095),
    .IN_WIDTH_BW(13),
    .IN_HEIGHT_BW(13)
)
u_vin_fmt_detect
(
    .I_video_pclk(pclk),
    .I_video_vsync(vin_vsync),
    .I_video_de(vin_de),
    .I_sclk(I_sclk),
    .I_rst_n(I_rst_n),
    .O_video_start(new_frame),
    .O_video_over(),
    .O_video_width(O_reg_rb_vin_width),
    .O_video_height(O_reg_rb_vin_height),
    .O_video_frame_rate(),
    .O_video_not_active()
);

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        begin
        reg_vin_width <= 60;
        reg_vin_height <= 60;
        end
    else if (O_new_frame)
        begin
        reg_vin_width <= O_reg_rb_vin_width;
        reg_vin_height <= O_reg_rb_vin_height;
        end

//--------------------------------------------------------------------
// new frame
//--------------------------------------------------------------------
always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_vib_use_c1 <= 1'b0;
    else if (new_frame && I_reg_vib_enable /*&& !vib_wr_mem_bsy_0 && !vib_wr_mem_bsy_1*/)
        O_vib_use_c1 <= ~O_vib_use_c1;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        new_frame_dly <= 1'b0;
    else
        new_frame_dly <= {new_frame_dly[6:0],new_frame};

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_new_frame <= 1'b0;
    else
        O_new_frame <= new_frame_dly[7]/* && !vib_wr_mem_bsy_0 && !vib_wr_mem_bsy_1*/;

//--------------------------------------------------------------------
// video input
//--------------------------------------------------------------------
always @(posedge pclk)
    begin
    de <= vin_de;
    if (!vin_de)
        de_cnt <= 'd0;
    else
        de_cnt <= de_cnt + 1'b1;
    data <= vin_data;
    end

always @(posedge pclk)
    begin
    fifo_wrreq <= de;
    fifo_wdata <= data;
    vin_vsync_pclk_dly <= {vin_vsync_pclk_dly[2:0],vin_vsync};
    end

always @(posedge pclk)
    begin
    if (vin_vsync_pclk_dly[3] && !vin_vsync_pclk_dly[2])
        even_line_pre <= 1'b0;
    else if (fifo_wrreq && !de)
        even_line_pre <= ~even_line_pre;
    end

always @(posedge pclk)
    even_line_pre_dly <= {even_line_pre_dly[6:0],even_line_pre};

always @(posedge pclk)
    even_line <= even_line_pre_dly[7];

//--------------------------------------------------------------------
// FIFO 0 & 1
//--------------------------------------------------------------------
always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        reset_fifo_n <= 1'b0;
    else
        reset_fifo_n <= !O_new_frame;

always @(posedge pclk)
    begin
    fifo_wdata_dly <= fifo_wdata;
    fifo_0_wrreq <= fifo_wrreq && !even_line;
    fifo_1_wrreq <= fifo_wrreq && even_line;
    end

vib_async_fifo u_vib_async_fifo_0(
    .aclr(!reset_fifo_n),
    .data(fifo_wdata_dly),
    .rdclk(I_sclk),
    .rdreq(fifo0_rdreq),
    .wrclk(pclk),
    .wrreq(fifo_0_wrreq),
    .q(fifo0_q),
    .rdempty(),
    .rdusedw(fifo0_usedw),
    .wrfull()
);

//async_fifo
//#(
//    .DATA_BITS(24),
//    .DEPTH_BITS(12)
//)
//u_async_fifo_0
//(
//    .I_rst_n(reset_fifo_n),
//    .I_wrclk(pclk),
//    .O_wrfull(),
//    .I_wrreq(fifo_0_wrreq),
//    .I_data(fifo_wdata_dly),
//    .O_wrusedw(),
//    .I_rdclk(I_sclk),
//    .O_rdempty(),
//    .I_rdreq(fifo0_rdreq),
//    .O_q(fifo0_q),
//    .O_rdusedw(fifo0_usedw)
//);

vib_async_fifo u_vib_async_fifo_1(
    .aclr(!reset_fifo_n),
    .data(fifo_wdata_dly),
    .rdclk(I_sclk),
    .rdreq(fifo1_rdreq),
    .wrclk(pclk),
    .wrreq(fifo_1_wrreq),
    .q(fifo1_q),
    .rdempty(),
    .rdusedw(fifo1_usedw),
    .wrfull()
);

//async_fifo
//#(
//    .DATA_BITS(24),
//    .DEPTH_BITS(12)
//)
//u_async_fifo_1
//(
//    .I_rst_n(reset_fifo_n),
//    .I_wrclk(pclk),
//    .O_wrfull(),
//    .I_wrreq(fifo_1_wrreq),
//    .I_data(fifo_wdata_dly),
//    .O_wrusedw(),
//    .I_rdclk(I_sclk),
//    .O_rdempty(),
//    .I_rdreq(fifo1_rdreq),
//    .O_q(fifo1_q),
//    .O_rdusedw(fifo1_usedw)
//);

//--------------------------------------------------------------------
// vib_wr_mem
//--------------------------------------------------------------------
vib_wr_mem
#(
    .FRAME1_START_ADDR(FRAME1_START_ADDR),
    .FOR_EVEN_LINE(0)
)
u_vib_wr_mem_0
(
    .I_sclk(I_sclk),
    .I_rst_n(I_rst_n),
    .I_new_frame(O_new_frame),
    .I_vib_use_c1(O_vib_use_c1),
    .O_fifo_rdreq(fifo0_rdreq),
    .I_fifo_q(fifo0_q),
    .I_fifo_usedw(fifo0_usedw),
    .O_sdram_req(O_sdm1_vib_req),
    .I_sdram_ack(I_sdm1_vib_ack),
    .I_sdram_wr_rd_end(I_sdm1_sdram_wr_rd_end),
    .O_sdram_wr_en(O_sdm1_sdram_wr_en),
    .O_sdram_start_addr(O_sdm1_sdram_start_addr),
    .O_sdram_length(O_sdm1_sdram_length),
    .I_sdram_ask_for_data(I_sdm1_sdram_ask_for_data),
    .O_sdram_wdata(O_sdm1_sdram_wdata),
    .I_reg_vib_enable(I_reg_vib_enable),
    .I_reg_vin_width(reg_vin_width),
    .I_reg_vin_height(reg_vin_height),
    .O_reg_rb_ddr3_err(reg_rb_ddr3_err_0)
);

vib_wr_mem
#(
    .FRAME1_START_ADDR(FRAME1_START_ADDR),
    .FOR_EVEN_LINE(1)
)
u_vib_wr_mem_1
(
    .I_sclk(I_sclk),
    .I_rst_n(I_rst_n),
    .I_new_frame(O_new_frame),
    .I_vib_use_c1(O_vib_use_c1),
    .O_fifo_rdreq(fifo1_rdreq),
    .I_fifo_q(fifo1_q),
    .I_fifo_usedw(fifo1_usedw),
    .O_sdram_req(O_sdm2_vib_req),
    .I_sdram_ack(I_sdm2_vib_ack),
    .I_sdram_wr_rd_end(I_sdm2_sdram_wr_rd_end),
    .O_sdram_wr_en(O_sdm2_sdram_wr_en),
    .O_sdram_start_addr(O_sdm2_sdram_start_addr),
    .O_sdram_length(O_sdm2_sdram_length),
    .I_sdram_ask_for_data(I_sdm2_sdram_ask_for_data),
    .O_sdram_wdata(O_sdm2_sdram_wdata),
    .I_reg_vib_enable(I_reg_vib_enable),
    .I_reg_vin_width(reg_vin_width),
    .I_reg_vin_height(reg_vin_height),
    .O_reg_rb_ddr3_err(reg_rb_ddr3_err_1)
);

always @(posedge I_sclk)
    O_reg_rb_ddr3_err <= reg_rb_ddr3_err_0 | reg_rb_ddr3_err_1;

endmodule
`default_nettype wire

